#include "ManejadorConteo.h"
#include <algorithm>

using namespace Manejadores;

ManejadorConteo::ManejadorConteo() {
	Configuracion* config = Configuracion::getInstancia();
	string pathDeDatos = config->getNombreArchivoConteo();
	this->conteos = new ArchivoDeEstructurasVariables(pathDeDatos);
	this->indicePorIDdeConteo = new HashExtensible("IndicePorUnicidadConteo");
	this->indicePorFechaYDistrito = new ArbolBMas("ConteoPorFechaYDistrito");
	this->indicePorEleccion = new ArbolBMas("ConteoPorEleccion");
	this->indicePorLista = new ArbolBMas("ConteoPorLista");
}

void ManejadorConteo::alta(string fecha, string cargo, string distrito, string nombreLista, int voto) {

	Conteo* conteo = new Conteo();
	conteo->setEleccion(atoi(fecha.c_str()), cargo);
	conteo->setLista(nombreLista);
	conteo->setDistrito(distrito);
	conteo->setCantidadDeVotos(voto);

	unsigned int posicionDelRegistro = conteos->insertar(conteo);

	string clavePorUnicidad = fecha + cargo + nombreLista + distrito;
	indicePorIDdeConteo->insertar(clavePorUnicidad, posicionDelRegistro);

	string claveIndiceFechaYDistrito = fecha + distrito;
	indicePorFechaYDistrito->insertar(claveIndiceFechaYDistrito, posicionDelRegistro);

	string claveIndiceEleccion = fecha + cargo;
	indicePorEleccion->insertar(claveIndiceEleccion, posicionDelRegistro);

	string claveIndiceLista = claveIndiceEleccion + nombreLista;
	indicePorLista->insertar(claveIndiceLista, posicionDelRegistro);

	indicePorIDdeConteo->exportarTxt("IndicePorUnicidadConteo");
	this->indicePorFechaYDistrito->exportar("ConteoPorFechaYDistrito");
	this->indicePorEleccion->exportar("ConteoPorEleccion");
	this->indicePorLista->exportar("ConteoPorLista");

	delete conteo;
}

void ManejadorConteo::registrarVoto(string fecha, string cargo, string distrito, string nombreLista) {

	string clavePorUnicidad = fecha + cargo + nombreLista + distrito;
	IRegistroIndice* registro = indicePorIDdeConteo->buscar(clavePorUnicidad);
	if (registro->getOffsets()->size() == 0) {
		this->alta(fecha, cargo, distrito, nombreLista, 1);
	} else {
		Conteo* conteo = new Conteo();
		this->conteos->leer(conteo, registro->getOffsets()->at(0));
		conteo->sumarUnVoto();
		unsigned int nuevoOffset = this->conteos->modificar(conteo, registro->getOffsets()->at(0));

		if (registro->getOffsets()->at(0) != nuevoOffset) {
			// actualizo los indices
			string clavePorUnicidad = fecha + cargo + nombreLista + distrito;
			indicePorIDdeConteo->modificar(clavePorUnicidad, registro->getOffsets()->at(0), nuevoOffset);

			string claveIndiceFechaYDistrito = fecha + distrito;
			indicePorFechaYDistrito->modificar(claveIndiceFechaYDistrito, registro->getOffsets()->at(0), nuevoOffset);

			string claveIndiceEleccion = fecha + cargo;
			indicePorEleccion->modificar(claveIndiceEleccion, registro->getOffsets()->at(0), nuevoOffset);

			string claveIndiceLista = claveIndiceEleccion + nombreLista;
			indicePorLista->modificar(claveIndiceLista, registro->getOffsets()->at(0), nuevoOffset);
		}

		delete conteo;
	}

	delete registro;

}

/*
 * Reportes del archivo conteo.
 */
bool sortFunctionPorCargo(Conteo* i, Conteo* j) {

	return (i->getEleccion()->getCargo()->getNombre() < j->getEleccion()->getCargo()->getNombre());
}

bool ManejadorConteo::reportePorDistrito(string distrito, string fecha) {

	fecha = Utils::aMinuscula(fecha);
	distrito = Utils::aMinuscula(distrito);

	bool resultado;
	Configuracion* config = Configuracion::getInstancia();
	string path = config->getPathReportes() + "reportePorDistrito.txt";
	fstream reportePorDistrito;

	string claveDeRecuperacion = fecha + distrito;
	IRegistroIndice* conteos = this->indicePorFechaYDistrito->buscar(claveDeRecuperacion);
	if (conteos != NULL) {
		if (claveDeRecuperacion == conteos->getClave()) {
			//abro el archivo
			resultado = true;
			reportePorDistrito.open(path.c_str(), ios::out | ios::trunc);
			if (reportePorDistrito.bad()) {
				reportePorDistrito.open(path.c_str(), ios::out);
			}

			string eleccionAnterior = "";
			string eleccionActual;
			string listaActual;
			string listaAnterior = "";
			int totalEleccion = 0;
			reportePorDistrito << "Informe de elecciones realizadas en el distrito: " << distrito << " el dia: "
					<< fecha << endl;

			vector<Conteo*>* vectorConteos = new vector<Conteo*> ;

			for (unsigned int i = 0; i < conteos->getOffsets()->size(); i++) {

				Conteo* conteo = new Conteo();
				this->conteos->leer(conteo, conteos->getOffsets()->at(i));

				vectorConteos->push_back(conteo);
				sort(vectorConteos->begin(), vectorConteos->end(), sortFunctionPorCargo);
			}

			for (unsigned int i = 0; i < vectorConteos->size(); i++) {

				string fechaEleccion = Utils::convertirEnteroAString(vectorConteos->at(i)->getEleccion()->getFecha());
				eleccionActual = fechaEleccion + vectorConteos->at(i)->getEleccion()->getCargo()->getNombre();

				if (eleccionAnterior != eleccionActual) {
					listaAnterior = "";
					if (eleccionAnterior != "") {
						reportePorDistrito << "Total de votos en la eleccion: " << Utils::convertirEnteroAString(
								totalEleccion) << endl;
						reportePorDistrito << " " << endl;
					}
					eleccionAnterior = eleccionActual;
					reportePorDistrito << "Elecciones para "
							<< vectorConteos->at(i)->getEleccion()->getCargo()->getNombre() << " el dia "
							<< fechaEleccion << endl;
					reportePorDistrito << " " << endl;
					totalEleccion = 0;
				}
				totalEleccion += vectorConteos->at(i)->getCantidadDeVotos();

				listaActual = vectorConteos->at(i)->getLista()->getNombre();

				if (listaActual != listaAnterior) {
					listaAnterior = listaActual;
					reportePorDistrito << "    La lista : " << listaActual << " obtuvo "
							<< vectorConteos->at(i)->getCantidadDeVotos() << " votos" << endl;
					reportePorDistrito << " " << endl;
				}

			}
			if (eleccionAnterior != "") {
				reportePorDistrito << "Total de votos en la eleccion: " << Utils::convertirEnteroAString(totalEleccion)
						<< endl;
			}

			for (unsigned int i = 0; i < vectorConteos->size(); i++) {
				delete vectorConteos->at(i);
			}
			delete vectorConteos;
			reportePorDistrito.close();
		}
		delete conteos;
	}

	return resultado;

}

bool sortFunctionPorLista(Conteo* i, Conteo* j) {

	return (i->getLista()->getNombre() < j->getLista()->getNombre());
}

bool ManejadorConteo::reportePorLista(string cargo, string fecha, string nombre) {

	fecha = Utils::aMinuscula(fecha);
	cargo = Utils::aMinuscula(cargo);
	nombre = Utils::aMinuscula(nombre);

	bool resultado = false;
	Configuracion* config = Configuracion::getInstancia();
	string path = config->getPathReportes() + "reportePorLista.txt";
	fstream reportePorLista;

	string claveDeRecuperacion = fecha + cargo + nombre;
	IRegistroIndice* conteos = this->indicePorLista->buscar(claveDeRecuperacion);

	if (conteos != NULL) {
		if (claveDeRecuperacion == conteos->getClave()) {

			reportePorLista.open(path.c_str(), ios::out | ios::trunc);
			if (reportePorLista.bad()) {
				reportePorLista.open(path.c_str(), ios::out);
			}

			resultado = true;
			string listaAnterior = "";
			string listaActual;
			reportePorLista << "Resultados obtenidos por la lista  " << nombre << " en las elecciones para  " << cargo
					<< " el dia " << fecha << endl;
			int totalListas = 0;

			vector<Conteo*>* vectorConteos = new vector<Conteo*> ;

			for (unsigned int i = 0; i < conteos->getOffsets()->size(); i++) {

				Conteo* conteo = new Conteo();
				this->conteos->leer(conteo, conteos->getOffsets()->at(i));

				vectorConteos->push_back(conteo);
				sort(vectorConteos->begin(), vectorConteos->end(), sortFunctionPorLista);
			}

			for (unsigned int i = 0; i < vectorConteos->size(); i++) {

				listaActual = vectorConteos->at(i)->getLista()->getNombre();

				totalListas += vectorConteos->at(i)->getCantidadDeVotos();

				if (listaActual != listaAnterior) {
					if (listaAnterior != "") {
						reportePorLista << "  Total de votos : " << Utils::convertirEnteroAString(totalListas) << endl;
						totalListas = 0;
					}
					listaAnterior = listaActual;
					reportePorLista << "    Lista : " << listaActual << endl;
					reportePorLista << " " << endl;
				}

			}
			if (listaAnterior != "") {
				reportePorLista << "  Total de votos : " << Utils::convertirEnteroAString(totalListas) << endl;
			}
			for (unsigned int i = 0; i < vectorConteos->size(); i++) {
				delete vectorConteos->at(i);
			}
			delete vectorConteos;

			reportePorLista.close();
		}
		delete conteos;
	}

	return resultado;
}

bool sortFunctionPorDistrito(Conteo* i, Conteo* j) {

	return (i->getDistrito()->getNombre() < j->getDistrito()->getNombre());
}

bool ManejadorConteo::reportePorEleccion(string cargo, string fecha) {

	fecha = Utils::aMinuscula(fecha);
	cargo = Utils::aMinuscula(cargo);

	bool resultado = false;
	Configuracion* config = Configuracion::getInstancia();
	string path = config->getPathReportes() + "reportePorEleccion.txt";
	fstream reportePorEleccion;

	string claveDeRecuperacion = fecha + cargo;
	IRegistroIndice* conteos = this->indicePorEleccion->buscar(claveDeRecuperacion);

	if (conteos != NULL) {
		if (claveDeRecuperacion == conteos->getClave()) {

			reportePorEleccion.open(path.c_str(), ios::out | ios::trunc);
			if (reportePorEleccion.bad()) {
				reportePorEleccion.open(path.c_str(), ios::out);
			}
			resultado = true;
			string distritoAnterior = "";
			string listaAnterior = "";
			string distritoActual;
			string listaActual;
			reportePorEleccion << "Informe de los resultados de las elecciones para " << cargo << " el dia " << fecha
					<< endl;

			vector<Conteo*>* vectorConteos = new vector<Conteo*> ;

			for (unsigned int i = 0; i < conteos->getOffsets()->size(); i++) {

				Conteo* conteo = new Conteo();
				this->conteos->leer(conteo, conteos->getOffsets()->at(i));

				vectorConteos->push_back(conteo);
				sort(vectorConteos->begin(), vectorConteos->end(), sortFunctionPorDistrito);
			}

			for (unsigned int i = 0; i < vectorConteos->size(); i++) {

				distritoActual = vectorConteos->at(i)->getDistrito()->getNombre();

				if (distritoAnterior != distritoActual) {
					distritoAnterior = distritoActual;
					listaAnterior = "";
					reportePorEleccion << "Distrito: " << vectorConteos->at(i)->getDistrito()->getNombre() << endl;
					reportePorEleccion << " " << endl;
				}

				listaActual = vectorConteos->at(i)->getLista()->getNombre();

				if (listaActual != listaAnterior) {
					listaAnterior = listaActual;
					reportePorEleccion << "    La lista : " << listaActual << " obtuvo "
							<< vectorConteos->at(i)->getCantidadDeVotos() << " votos" << endl;
					reportePorEleccion << " " << endl;
				}
			}

			for (unsigned int i = 0; i < vectorConteos->size(); i++) {
				delete vectorConteos->at(i);
			}
			delete vectorConteos;
			reportePorEleccion.close();
		}
		delete conteos;
	}

	return resultado;

}

ManejadorConteo::~ManejadorConteo() {
	delete this->conteos;
	delete this->indicePorLista;
	delete this->indicePorIDdeConteo;
	delete this->indicePorFechaYDistrito;
	delete this->indicePorEleccion;

}
